#!/bin/bash

# 显示详细帮助信息
show_help() {
  cat << EOF
用法: $(basename "$0") [选项]

功能: 交互式清理Docker镜像，保留指定数量的最新镜像，同时可清理悬空镜像

选项:
  -h, --help           显示此详细帮助信息
  -d, --dry-run        试运行模式，只显示要删除的镜像，不实际执行删除操作

操作流程:
  1. 运行脚本后，系统会提示您输入要清理的镜像模式和保留数量
  2. 可输入多个镜像配置，完成后通过空输入结束配置
  3. 系统会显示所有配置并请您确认
  4. 确认后开始清理指定镜像
  5. 最后会处理悬空镜像（<none>:<none>）并询问是否删除

镜像模式说明:
  - 可以是完整的镜像名称，如 "jiaming-api"
  - 可以包含通配符，如 "jiaming-*" 匹配所有以jiaming-开头的镜像
  - 可以指定仓库和标签，如 "myregistry.com/jiaming-api:v*"
  - 支持Docker镜像引用的标准格式

保留数量说明:
  - 必须是一个非负整数（0或正整数）
  - 表示要保留的最新镜像数量，超过此数量的旧镜像将被删除
  - 示例：输入3表示保留最新的3个镜像，删除更旧的镜像

使用示例:
  1. 正常运行并清理镜像:
     $(basename "$0")
  
  2. 试运行查看效果（不实际删除）:
     $(basename "$0") --dry-run
  
  3. 清理示例:
     - 输入镜像模式: jiaming-api
     - 输入保留数量: 3
     - 输入镜像模式: jiaming-admin
     - 输入保留数量: 2
     - 输入镜像模式: （直接回车结束输入）

提示:
  - 不确定镜像名称时，可以先运行 'docker images' 查看所有镜像
  - 建议先使用 --dry-run 模式预览清理效果，确认无误后再实际执行
  - 悬空镜像（<none>:<none>）通常是构建过程中产生的中间镜像，可以安全删除
EOF
}

# 显示简短操作提示（在交互式输入时显示）
show_short_help() {
  cat << EOF
操作提示:
- 输入镜像模式（如"jiaming-api"或"myapp/*"）
- 输入保留数量（非负整数，如3表示保留最新3个）
- 输入空的镜像模式结束配置输入
- 随时可以按 Ctrl+C 取消操作
EOF
}

# 默认配置
DRY_RUN=0

# 解析命令行参数
while [[ $# -gt 0 ]]; do
  case "$1" in
    -h|--help)
      show_help
      exit 0
      ;;
    -d|--dry-run)
      DRY_RUN=1
      shift
      ;;
    --)
      shift
      break
      ;;
    *)
      echo "错误: 未知选项 $1"
      show_help
      exit 1
      ;;
  esac
done

# 显示已输入的配置
display_configs() {
  local configs=("$@")
  local count=$(( ${#configs[@]} / 2 ))
  
  if [ $count -eq 0 ]; then
    echo "尚未输入任何配置"
    return
  fi
  
  echo "已输入的配置:"
  for ((i=0; i<count; i++)); do
    local index=$((i + 1))
    local pattern=${configs[$((i * 2))]}
    local keep=${configs[$((i * 2 + 1))]}
    echo "  $index. 镜像模式: $pattern, 保留数量: $keep"
  done
}

# 移除旧镜像的函数
remove_old_images() {
  local pattern=$1
  local keep=$2
  echo "----------------------------------------"
  echo "处理匹配模式: $pattern (保留最新的 $keep 个镜像)"

  # 获取按创建时间排序的镜像ID（最新的在前）
  images=$(docker images --format "{{.ID}}" --filter "reference=$pattern" | 
           xargs -I {} docker inspect --format '{{.Id}} {{.Created}}' {} | 
           sort -k2 -r | 
           awk '{print $1}')

  # 统计镜像数量
  total_images=$(echo "$images" | wc -l | tr -d ' ')
  
  if [ "$total_images" -le "$keep" ]; then
    echo "找到 $total_images 个镜像，小于等于保留数量 $keep，不删除任何镜像"
    return
  fi

  # 提取需要删除的镜像ID（除了最新的$keep个）
  images_to_delete=$(echo "$images" | tail -n +$((keep + 1)))
  
  if [ -z "$images_to_delete" ]; then
    echo "没有需要删除的镜像"
    return
  fi

  # 显示要删除的镜像
  echo "将删除以下 $((total_images - keep)) 个旧镜像:"
  echo "$images_to_delete" | while read -r img_id; do
    # 显示镜像的详细信息
    docker images --filter "id=$img_id" --format "  {{.Repository}}:{{.Tag}} ({{.ID}})"
  done

  # 执行删除（如果不是试运行）
  if [ "$DRY_RUN" -eq 0 ]; then
    echo "正在删除..."
    echo "$images_to_delete" | xargs docker image rm
    echo "删除完成"
  else
    echo "试运行模式: 不会实际删除镜像"
  fi
}

# 处理悬空镜像
remove_dangling_images() {
  echo "----------------------------------------"
  echo "处理悬空镜像（<none>:<none>）"
  echo "提示: 悬空镜像是未被任何标签引用的镜像，通常是构建残留可以安全删除"
  
  none_images=$(docker images --filter "dangling=true" --format "{{.ID}}")
  
  if [ -z "$none_images" ]; then
    echo "没有悬空镜像需要删除"
    return
  fi

  # 显示要删除的悬空镜像数量
  count=$(echo "$none_images" | wc -l | tr -d ' ')
  echo "找到 $count 个悬空镜像"

  # 询问是否删除悬空镜像
  read -p "是否删除这些悬空镜像? [y/N] " -n 1 -r
  echo
  if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    echo "已跳过删除悬空镜像"
    return
  fi

  # 执行删除（如果不是试运行）
  if [ "$DRY_RUN" -eq 0 ]; then
    echo "正在删除悬空镜像..."
    echo "$none_images" | xargs docker image rm
    echo "悬空镜像删除完成"
  else
    echo "试运行模式: 不会实际删除悬空镜像"
  fi
}

# 交互式输入配置
interactive_input() {
  local configs=()  # 存储格式: pattern1 keep1 pattern2 keep2 ...
  local index=0

  echo "欢迎使用Docker镜像清理工具"
  echo "----------------------------------------"
  show_short_help
  echo "----------------------------------------"
  echo

  while true; do
    # 显示已输入的配置
    echo "----------------------------------------"
    display_configs "${configs[@]}"
    echo "----------------------------------------"
    
    # 输入镜像模式
    read -p "请输入第 $((index + 1)) 个镜像模式 (空输入结束): " pattern
    
    # 空输入表示结束
    if [ -z "$pattern" ]; then
      if [ $index -eq 0 ]; then
        echo "没有输入任何镜像模式，退出脚本"
        exit 0
      fi
      break
    fi

    # 输入保留数量
    while true; do
      read -p "请输入要保留的最新镜像数量 (非负整数): " keep
      if [[ "$keep" =~ ^[0-9]+$ ]] && [ "$keep" -ge 0 ]; then
        break
      else
        echo "无效的数量，请输入一个非负整数（如0、1、2、3等）"
      fi
    done

    # 保存配置
    configs+=("$pattern" "$keep")
    index=$((index + 1))
    echo
    echo "已添加配置: 镜像模式: $pattern, 保留数量: $keep"
    echo
  done

  # 显示最终输入的配置并确认
  echo "----------------------------------------"
  echo "你输入的所有清理配置如下:"
  display_configs "${configs[@]}"

  read -p "是否确认执行这些清理操作? [y/N] " -n 1 -r
  echo
  if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    echo "已取消操作"
    exit 0
  fi

  # 处理每个配置
  local count=$(( ${#configs[@]} / 2 ))
  for ((i=0; i<count; i++)); do
    local pattern=${configs[$((i * 2))]}
    local keep=${configs[$((i * 2 + 1))]}
    remove_old_images "$pattern" "$keep"
  done
}

# 主执行流程
echo "===== 开始交互式Docker镜像清理 ====="
interactive_input
remove_dangling_images
echo "===== Docker镜像清理完成 ====="
